﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace LabMonitorRemote
{
    public class DewkMeasurment
    {
        const Int32 _Port = 10001;

        private IPEndPoint _IpEndPoint;
        private string _LoggerName;
        private string _IpAddress;
        private DateTime _MeasurmentTime;
        private Boolean _DisplayF;
        private string _Ch1TempString;
        private string _Ch2TempString;
        private string _Ch1HumidString;
        private string _Ch2HumidString;
        private Double _Ch1Temp;
        private Double _Ch2Temp;
        private Double _Ch1Humid;
        private Double _Ch2Humid;
        private frmMain _ParentForm;

        public DewkMeasurment(DewkLogger Dewk)
        {
            this.LoggerName = Dewk.LoggerName;
            this.LoggerIpAddress = Dewk.IpAddress;
        }
        public DewkMeasurment(DewkLogger Dewk,frmMain parentForm)
        {
            this.LoggerName = Dewk.LoggerName;
            this.LoggerIpAddress = Dewk.IpAddress;
            this.ParentForm = parentForm;
        }
        public DewkMeasurment(string IpAddress)
        {
            this.LoggerIpAddress = IpAddress;
        }

        #region Properties
        public DewkLogger Dewk
        {
            get;
            set;
        }
        public IPEndPoint IpEndPoint
        {
            get {return _IpEndPoint;}
            private set {_IpEndPoint = value;}
        }
        public string LoggerName
        {
            get {return _LoggerName;}
            private set { _LoggerName = value; }
        }
        public string LoggerIpAddress
        {
            get { return _IpAddress; }
            private set
            {
                if (ValidIPAddress(value))
                {
                    _IpAddress = value;
                    _IpEndPoint = new IPEndPoint(IPAddress.Parse(value), _Port);
                }
                else
                {
                    Console.WriteLine("The address entered {0} is not a valid IPv4 address.", value);
                }
            }
        }
        public DateTime MeasurmentTime
        {
            get { return _MeasurmentTime; }
            private set { _MeasurmentTime = value; }
        }
        public Boolean DisplayF
        {
            get { return _DisplayF; }
            private set { _DisplayF = value; }
        }
        private string Ch1TemperatureString
        {
            get { return _Ch1TempString; }
            set
            {
                if (value.Length > 1)
                {
                    _Ch1TempString = value;
                    Ch1Temperature = Convert.ToDouble(value);
                }
                else
                {
                    _Ch1TempString = "N/C";
                }
            }
        }
        private string Ch2TemperatureString
        {
            get { return _Ch2TempString; }
            set
            {
                if (value.Length > 1)
                {
                    _Ch2TempString = value;
                    Ch2Temperature = Convert.ToDouble(value);
                }
                else
                {
                    _Ch2TempString = "N/C";
                }
            }
        }
        private string Ch1HumidityString
        {
            get { return _Ch1HumidString; }
            set
            {
                if (value.Length > 1)
                {
                    _Ch1HumidString = value;
                    Ch1Humidity = Convert.ToDouble(value);
                }
                else
                {
                    _Ch1HumidString = "N/C";
                }
            }
        }
        private string Ch2HumidityString
        {
            get { return _Ch2HumidString; }
            set
            {
                if (value.Length > 1)
                {
                    _Ch2HumidString = value;
                    Ch2Humidity = Convert.ToDouble(value);
                }
                else
                {
                    _Ch2HumidString = "N/C";
                }
            }
        }
        public Double Ch1Temperature
        {
            get { return _Ch1Temp; }
            private set { _Ch1Temp = value; }
        }
        public Double Ch2Temperature
        {
            get { return _Ch2Temp; }
            private set { _Ch2Temp = value; }
        }
        public Double Ch1Humidity
        {
            get { return _Ch1Humid; }
            private set { _Ch1Humid = value; }
        }
        public Double Ch2Humidity
        {
            get { return _Ch2Humid; }
            private set { _Ch2Humid = value; }
        }
        public frmMain ParentForm
        {
            get { return _ParentForm; }
            set { _ParentForm = value; }
        }
        #endregion Properties

        #region Events
        public event MeasurmentStartingEventHandler MeasurmentStarting;
        public delegate void MeasurmentStartingEventHandler(DewkMeasurment dm, EventArgs e);
        private void OnMeasurmentStarting()
        {
            if (MeasurmentStarting != null)
            {
                MeasurmentStarting(this, new EventArgs());
            }
        }

        public event MeasurmentCompleteEventHandler MeasurmentComplete;
        public delegate void MeasurmentCompleteEventHandler(DewkMeasurment dm, EventArgs e);
        private void OnMeasurmentComplete()
        {
            if (MeasurmentComplete != null)
            {
                MeasurmentComplete(this, new EventArgs());
            }
        }

        public event MeasurmentFailedEventHandler MeasurmentFailed;
        public delegate void MeasurmentFailedEventHandler(DewkMeasurment dm, EventArgs e);
        private void OnMeasurmentFailed()
        {
            if (MeasurmentFailed != null)
            {
                MeasurmentFailed(this, new EventArgs());
            }
        }
        #endregion Events

        private bool ValidIPAddress(string ip)
        {
            string[] octets = ip.Split(".".ToCharArray());
            if (octets.Length < 4)
            {
                // not a valid IPv4 string in X.X.X.X format
                return false;
            }
            else
                foreach (string octet in octets)
                {
                    byte checkPart = 0;
                    if (!byte.TryParse(octet, out checkPart))
                    {
                        // not a valid IPv4 string in X.X.X.X format
                        return false;
                    }                    
                }
            // string is a valid IPv4 string in X.X.X.X format
            return true;
        }

        public void GetCurrentMeasurment()
        {
            // Taking Measurment
            OnMeasurmentStarting();
            if (TestCommunication())
                GetMeasurment();
        }

        private Boolean TestCommunication()
        {
            // Test Communications before attempting download
            DewkPing dewkPing = new DewkPing(this.LoggerName, this.LoggerIpAddress);
            dewkPing.TestCommunications();

            if (dewkPing.IsConnected)
                return true;
            else
                OnMeasurmentFailed();
                return false;
        }

        private void GetMeasurment()
        {
            // GET MEASURMENT SCALE UNIT
            GetDisplayUnit getDisplayUnit = new GetDisplayUnit(_IpEndPoint);
                //subscribe
            getDisplayUnit.CommandSent += new DewkCommand.CommandSentEventHandler(RespondToCommandSent);

            ReadSimpleDataDewkRead readDisplayUnit = new ReadSimpleDataDewkRead(getDisplayUnit.TCPClient, getDisplayUnit.NetStream);
                //subscribe
            readDisplayUnit.ReadComplete += new DewkRead.ReadCompleteEventHandler(RespondToReadComplete);

            getDisplayUnit.WriteCommand();
            readDisplayUnit.Read();
            DecodeMeasurmentUnitBytes(readDisplayUnit.ReadBuffer, readDisplayUnit.NumBytesRead);
            

            // GET MEASURMENT VALUES
            GetMeasurmentDewkCommand GetMeasCmd = new GetMeasurmentDewkCommand(_IpEndPoint);
                //subscribe
            GetMeasCmd.CommandSent += new DewkCommand.CommandSentEventHandler(RespondToCommandSent);
            
            GetMeasurmentDewkRead GetMeasRead = new GetMeasurmentDewkRead(GetMeasCmd.TCPClient, GetMeasCmd.NetStream);
                //subscribe
            GetMeasRead.ReadComplete += new DewkRead.ReadCompleteEventHandler(RespondToReadComplete);
            
            GetMeasCmd.WriteCommand();
            GetMeasRead.Read();
            DecodeMeasBytes(GetMeasRead.ReadBuffer, GetMeasRead.NumBytesRead);
        }

        private void DecodeMeasurmentUnitBytes(byte[] array, Int32 length)
        {
            char[] UnitArray;
            //string UnitString = "";
            UnitArray = System.Text.Encoding.ASCII.GetChars(array);

            /*
            int i = 0;
            while (UnitArray[i] != 13 && i < length)
            {
                UnitString += UnitArray[i].ToString();
                i++;
            }
            */

            if (UnitArray[0].ToString() == "C")
            {
                this.DisplayF = false;
            }
            if (UnitArray[0].ToString() == "F")
            {
                this.DisplayF = true;
            }
        }

        private void DecodeMeasBytes(byte[] array, Int32 length)
        {
            char[] MeasArray;            
            string MeasString = "";
            
            MeasArray = System.Text.Encoding.ASCII.GetChars(array);
             
            Int32 i = 0;
            while (MeasArray[i] != 13 && i < length)
            {
                MeasString += MeasArray[i].ToString();
                i++;
            }
            
            //_ParentForm.WriteNetworkFeedback(MeasString);
            
            string[] MeasParts = MeasString.Split(",".ToCharArray());
            if (MeasParts.Length < 4) // if data does not reflect full measurment read...
            {
                GetMeasurment(); // attempt process again
            }
            else
            {
                SetMeasurmentValues(MeasParts);
            }
        }

        private void SetMeasurmentValues(string[] MeasParts)
        {
            // Example Reading:
            // 23.89,31.4,0,0
            Ch1TemperatureString = MeasParts[0];
            Ch1HumidityString = MeasParts[1];
            Ch2TemperatureString = MeasParts[2];
            Ch2HumidityString = MeasParts[3];

            // Measurment Complete
            MeasurmentTime = DateTime.Now;
            OnMeasurmentComplete();
        }

        private void RespondToCommandSent(object sender, EventArgs e)
        {
            if (_ParentForm != null)
            {
                string message = string.Format(
                    "SENDING {0}: {1}", ((DewkCommand)sender).IpEndPoint.Address, ((DewkCommand)sender).CommandToSend);
                _ParentForm.WriteNetworkFeedback(message);
            }
        }
        private void RespondToReadComplete(object sender, EventArgs e)
        {
            if (_ParentForm != null)
            {
                _ParentForm.WriteNetworkFeedback(this.LoggerIpAddress, ((DewkRead)sender).ReadBuffer, ((DewkRead)sender).NumBytesRead);
            }
        }


    }
}
